#ifndef Matran_H
#define Matran_H
#include <iostream>

using namespace std;

#define ROW 0
#define COLUMN 1
template <class T> class Matran
{
    public:
        /** Default constructor */
        Matran();
        Matran(int, int);
        Matran(int);
        /** Default destructor */
        virtual ~Matran(){}
        virtual void Set(int, int, T);
        T Get(int, int);
        void operator = (Matran<T> other);
        void AssignedBy(Matran<T> other);
        Matran<T> operator + (Matran<T> other);
        Matran<T> operator - (Matran<T> other);
        Matran<T> operator * (Matran<T> other);
        Matran<T> operator * (double scalar);
        int GetSize(int dimension);
        void Print();
    protected:
        T ** data;
        int rows;
        int columns;
    private:
};
//Constructors
template <class T> Matran<T>::Matran()
{

}
template <class T> Matran<T>::Matran(int degree)
{
    rows = degree;
    columns = degree;

    data = new T* [rows];
    for(int i = 0; i < rows; i++)
        data[i] = new T [columns];

    for(int i = 0; i < rows; i++)
        for(int j = 0; j < columns; j++)
            data[i][j] = (T)0;
}
template <class T> Matran<T>::Matran(int _rows, int _columns)
{
    rows = _rows;
    columns = _columns;

    data = new T* [rows];
    for(int i = 0; i < rows; i++)
        data[i] = new T [columns];

    for(int i = 0; i < rows; i++)
        for(int j = 0; j < columns; j++)
            data[i][j] = (T)0;
}
//End Constructors
//Methods
template <class T> void Matran<T>::Set(int i, int j, T value)
{
    data[i][j] = value;
}
template <class T> T Matran<T>::Get(int i, int j)
{
    return data[i][j];
}
template <class T> void Matran<T>::operator = (Matran<T> other)
{
    if(this->rows != other.rows || this->columns != other.columns)
    {
        cout << "Dimensions must agree!";
        throw;
    }
    else
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < columns; j++)
                this->data[i][j] = other.data[i][j];
}
template <class T> void Matran<T>::AssignedBy(Matran<T> other)
{
    if(this->rows != other.rows || this->columns != other.columns)
    {
        cout << "Dimensions must agree!";
        //exit(0);
    }
    else
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < columns; j++)
                this->data[i][j] = other.data[i][j];
}
template <class T> Matran<T> Matran<T>::operator + (Matran<T> other)
{
    if(this->rows != other.rows || this->columns != other.columns)
    {
        cout << "Dimensions must agree!";
        throw;
    }
    else
    {
        Matran<T> sum(this->rows, this->columns);
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < columns; j++)
                sum.data[i][j] = this->data[i][j] + other.data[i][j];
        return sum;
    }
}
template <class T> Matran<T> Matran<T>::operator - (Matran<T> other)
{
    if(this->rows != other.rows || this->columns != other.columns)
    {
        cout << "Dimensions must agree!";
        throw;
    }
    else
    {
        Matran<T> diff(this->rows, this->columns);
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < columns; j++)
                diff.data[i][j] = this->data[i][j] - other.data[i][j];
        return diff;
    }
}
template <class T> Matran<T> Matran<T>::operator*(Matran<T> other)
{
    if(this->columns != other.rows)
    {
        cout << "Dimensions must agree!";
        throw;
    }
    else
    {
        Matran<T> prod(this->rows, other.columns);

        for(int i = 0; i < this->rows; i++)
            for(int j = 0; j < other.columns; j++)
                for(int k = 0; k < this->columns; k++)
                    prod.data[i][j] += this->data[i][k]*other.data[k][j];

        return prod;
    }
}
template <class T> Matran<T> Matran<T>::operator*(double scalar)
{
    Matran<T> prod(this->rows, this->columns);

    for(int i = 0; i < rows; i++)
        for(int j = 0; j < columns; j++)
            prod.data[i][j] = (T)(scalar * this->data[i][j]);

    return prod;
}
template <class T> int Matran<T>::GetSize(int dimension)
{
    if(dimension == ROW)
        return rows;
    else
        return columns;
}
template <class T> void Matran<T>::Print()
{
    cout << "\n";
    for(int i = 0; i < rows; i ++)
    {
        for(int j = 0; j < columns; j++)
            cout << data[i][j] << "\t";
        cout << endl;
    }
    cout << "\n";
}
//End Methods
#endif // Matran_H
